#pragma once

#pragma unmanaged

#include <BWSAL\UnitGroup.h>

#pragma managed

#include "..\BWAPI\Unit.h"
#include "..\BWTA\BaseLocation.h"
#include "..\BWTA\Chokepoint.h"
#include "..\BWTA\Polygon.h"

using namespace System::Collections::Generic;
using namespace System::Runtime::CompilerServices;
using namespace BroodWar::Api;
using namespace BroodWar::Terrain;

namespace BroodWar
{
	namespace Addon
	{
		public enum class FilterFlag
		{
			exists = 1,
			isAccelerating,
			isAttacking,
			isBeingConstructed,
			isBeingGathered,
			isBeingHealed,
			isBlind,
			isBraking,
			isBurrowed,
			isCarryingGas,
			isCarryingMinerals,
			isCloaked,
			isCompleted,
			isConstructing,
			isDefenseMatrixed,
			isDetected,
			isEnsnared,
			isFollowing,
			isGatheringGas,
			isGatheringMinerals,
			isHallucination,
			isHoldingPosition,
			isIdle,
			isInterruptible,
			isIrradiated,
			isLifted,
			isLoaded,
			isLockedDown,
			isMaelstrommed,
			isMorphing,
			isMoving,
			isParasited,
			isPatrolling,
			isPlagued,
			isRepairing,
			isResearching,
			isSelected,
			isSieged,
			isStartingAttack,
			isStasised,
			isStimmed,
			isStuck,
			isTraining,
			isUnderStorm,
			isUnpowered,
			isUpgrading,
			isVisible,

			canProduce,
			canAttack,
			canMove,
			isFlyer,
			regeneratesHP,
			isSpellcaster,
			hasPermanentCloak,
			isInvincible,
			isOrganic,
			isMechanical,
			isRobotic,
			isDetector,
			isResourceContainer,
			isResourceDepot,
			isRefinery,
			isWorker,
			requiresPsi,
			requiresCreep,
			isTwoUnitsInOneEgg,
			isBurrowable,
			isCloakable,
			isBuilding,
			isAddon,
			isFlyingBuilding,
			isNeutral,
			isHero,
			isPowerup,
			isBeacon,
			isFlagBeacon,
			isSpecialBuilding,
			isSpell,

			Firebat,
			Ghost,
			Goliath,
			Marine,
			Medic,
			SCV,
			Siege_Tank,
			Vulture,
			Vulture_Spider_Mine,
			Battlecruiser,
			Dropship,
			Nuclear_Missile,
			Science_Vessel,
			Valkyrie,
			Wraith,
			Alan_Schezar,
			Alexei_Stukov,
			Arcturus_Mengsk,
			Edmund_Duke,
			Gerard_DuGalle,
			Gui_Montag,
			Hyperion,
			Jim_Raynor_Marine,
			Jim_Raynor_Vulture,
			Magellan,
			Norad_II,
			Samir_Duran,
			Sarah_Kerrigan,
			Tom_Kazansky,
			Civilian,
			Academy,
			Armory,
			Barracks,
			Bunker,
			Command_Center,
			Engineering_Bay,
			Factory,
			Missile_Turret,
			Refinery,
			Science_Facility,
			Starport,
			Supply_Depot,
			Comsat_Station,
			Control_Tower,
			Covert_Ops,
			Machine_Shop,
			Nuclear_Silo,
			Physics_Lab,
			Crashed_Norad_II,
			Ion_Cannon,
			Power_Generator,
			Psi_Disrupter,

			Archon,
			Dark_Archon,
			Dark_Templar,
			Dragoon,
			High_Templar,
			Probe,
			Reaver,
			Scarab,
			Zealot,
			Arbiter,
			Carrier,
			Corsair,
			Interceptor,
			Observer,
			Scout,
			Shuttle,
			Aldaris,
			Artanis,
			Danimoth,
			Hero_Dark_Templar,
			Fenix_Dragoon,
			Fenix_Zealot,
			Gantrithor,
			Mojo,
			Raszagal,
			Tassadar,
			Tassadar_Zeratul_Archon,
			Warbringer,
			Zeratul,
			Arbiter_Tribunal,
			Assimilator,
			Citadel_of_Adun,
			Cybernetics_Core,
			Fleet_Beacon,
			Forge,
			Gateway,
			Nexus,
			Observatory,
			Photon_Cannon,
			Pylon,
			Robotics_Facility,
			Robotics_Support_Bay,
			Shield_Battery,
			Stargate,
			Templar_Archives,
			Khaydarin_Crystal_Form,
			Protoss_Temple,
			Stasis_Cell_Prison,
			Warp_Gate,
			XelNaga_Temple,

			Broodling,
			Defiler,
			Drone,
			Egg,
			Hydralisk,
			Infested_Terran,
			Larva,
			Lurker,
			Lurker_Egg,
			Ultralisk,
			Zergling,
			Cocoon,
			Devourer,
			Guardian,
			Mutalisk,
			Overlord,
			Queen,
			Scourge,
			Devouring_One,
			Hunter_Killer,
			Infested_Duran,
			Infested_Kerrigan,
			Kukulza_Guardian,
			Kukulza_Mutalisk,
			Matriarch,
			Torrasque,
			Unclean_One,
			Yggdrasill,
			Creep_Colony,
			Defiler_Mound,
			Evolution_Chamber,
			Extractor,
			Greater_Spire,
			Hatchery,
			Hive,
			Hydralisk_Den,
			Infested_Command_Center,
			Lair,
			Nydus_Canal,
			Queens_Nest,
			Spawning_Pool,
			Spire,
			Spore_Colony,
			Sunken_Colony,
			Ultralisk_Cavern,
			Cerebrate,
			Cerebrate_Daggoth,
			Mature_Chrysalis,
			Overmind,
			Overmind_Cocoon,
			Overmind_With_Shell,

			Bengalaas,
			Kakaru,
			Ragnasaur,
			Rhynadon,
			Scantid,
			Ursadon,

			Mineral_Field,
			Vespene_Geyser,

			Dark_Swarm,
			Disruption_Web,
			Scanner_Sweep,

			Protoss_Beacon,
			Protoss_Flag_Beacon,
			Terran_Beacon,
			Terran_Flag_Beacon,
			Zerg_Beacon,
			Zerg_Flag_Beacon,

			Powerup_Data_Disk,
			Powerup_Flag,
			Powerup_Khalis_Crystal,
			Powerup_Khaydarin_Crystal,
			Powerup_Psi_Emitter,
			Powerup_Uraj_Crystal,
			Powerup_Young_Chrysalis,

			None,
			Unknown_Unit
		};
		
		public enum class FliterAttributeScalar
		{
			HitPoints,
			InitialHitPoints,
			Shields,
			Energy,
			Resources,
			InitialResources,
			KillCount,
			GroundWeaponCooldown,
			AirWeaponCooldown,
			SpellCooldown,
			DefenseMatrixPoints,
			DefenseMatrixTimer,
			EnsnareTimer,
			IrradiateTimer,
			LockdownTimer,
			MaelstromTimer,
			PlagueTimer,
			RemoveTimer,
			StasisTimer,
			StimTimer,
			PositionX,
			PositionY,
			InitialPositionX,
			InitialPositionY,
			TilePositionX,
			TilePositionY,
			InitialTilePositionX,
			InitialTilePositionY,
			Angle,
			VelocityX,
			VelocityY,
			TargetPositionX,
			TargetPositionY,
			OrderTimer,
			RemainingBuildTime,
			RemainingTrainTime,
			TrainingQueueCount,
			LoadedUnitsCount,
			InterceptorCount,
			ScarabCount,
			SpiderMineCount,
			RemainingResearchTime,
			RemainingUpgradeTime,
			RallyPositionX,
			RallyPositionY
		};

		public enum class FilterAttributeUnit
		{
			GetTarget,
			GetOrderTarget,
			GetBuildUnit,
			GetTransport,
			GetRallyUnit,
			GetAddon
		};
		
		public enum class FilterAttributeType
		{
			GetType,
			GetInitialType,
			GetBuildType,
			GetTech,
			GetUpgrade
		};

		public enum class FilterAttributePosition
		{
			GetPosition,
			GetInitialPosition,
			GetTargetPosition,
			GetRallyPosition
		};

		public enum class FilterAttributeTilePosition
		{
			GetTilePosition,
			GetInitialTilePosition
		};
		
		public enum class FilterAttributeOrder
		{
			GetOrder,
			GetSecondaryOrder
		};

		public ref class UnitGroup : public ICollection<Unit^>
		{
		internal:
			bool dispose;
			BWSAL::UnitGroup* instance;

			UnitGroup(BWSAL::UnitGroup* group);
			UnitGroup(BWSAL::UnitGroup group);
			~UnitGroup();
			!UnitGroup();
		public:
			#pragma region IList implementation
			virtual void Add(Unit^ item);
			virtual void Clear();
			virtual bool Contains(Unit^ item);
			virtual void CopyTo(array<Unit^>^ target, int arrayIndex);
			virtual IEnumerator<Unit^>^ GetEnumerator();
			virtual bool Remove(Unit^ item);
			
			virtual property int Count { int get(); }
			virtual property bool IsReadOnly { bool get(); }

		private:
			virtual System::Collections::IEnumerator^ GetEnumerator2() sealed = System::Collections::IEnumerable::GetEnumerator;
		
		public:
			#pragma endregion

			Unit^ NearestUnit(Position^ position);
			property Position^ Center { Position^ get(); }

			/// <summary>
			/// Orders the unit to attack move to the specified location
			/// </summary>
			bool Attack(Position^ target, bool shiftQueueCommand);
			bool Attack(Position^ target);

			/// <summary>
			/// Orders the unit to attack the specified unit.
			/// </summary>
			bool Attack(Unit^ target, bool shiftQueueCommand);
			bool Attack(Unit^ target);

			/// <summary>
			/// Orders the unit to build the given unit type at the given position. Note that if the player does not
			/// have enough resources when the unit attempts to place the building down, the order will fail. The
			/// tile position specifies where the top left corner of the building will be placed.
			/// </summary>
			bool Build(TilePosition^ target, Api::Enum::UnitType type);

			/// <summary>
			/// Orders the unit to build the given addon. The unit must be a Terran building that can have an addon
			/// and the specified unit type must be an addon unit type
			/// </summary>
			bool BuildAddon(Api::Enum::UnitType type);

			/// <summary>
			/// Orders this unit to add the specified unit type to the training queue. Note that the player must
			/// have sufficient resources to train. If you wish to make units from a hatchery, use getLarva to get
			/// the larva associated with the hatchery and then call morph on the larva you want to morph. This
			/// command can also be used to make interceptors and scarabs
			/// </summary>
			bool Train(Api::Enum::UnitType type);

			/// <summary>
			/// Orders the unit to morph into the specified unit type. Returns false if given a wrong type.
			/// \see Unit::cancelMorph, Unit::isMorphing
			/// </summary>
			bool Morph(Api::Enum::UnitType type);

			/// <summary>
			/// Orders the unit to research the given tech type.
			/// \see Unit::cancelResearch, Unit::Unit#isResearching, Unit::getRemainingResearchTime, Unit::getTech
			/// </summary>
			bool Research(Api::Enum::TechType type);

			/// <summary>
			/// Orders the unit to upgrade the given upgrade type.
			/// \see Unit::cancelUpgrade, Unit::Unit#isUpgrading, Unit::getRemainingUpgradeTime, Unit::getUpgrade
			/// </summary>
			bool Upgrade(Api::Enum::UpgradeType upgrade);

			/// <summary>
			/// Orders the unit to set its rally position to the specified position.
			/// \see Unit::getRallyPosition, Unit::getRallyUnit
			/// </summary>
			bool SetRallyPoint(Position^ target);

			/// <summary>
			/// Orders the unit to set its rally unit to the specified unit.
			/// \see Unit::setRallyPosition, Unit::getRallyPosition, Unit::getRallyUnit
			/// </summary>
			bool SetRallyPoint(Unit^ target);

			/// <summary>
			/// Orders the unit to move from its current position to the specified position.
			/// \see Unit::isMoving
			/// </summary>
			bool Move(Position^ target, bool shiftQueueCommand);
			bool Move(Position^ target);

			/// <summary>
			/// Orders the unit to patrol between its current position and the specified position.
			/// \see Unit::isPatrolling
			/// </summary>
			bool Patrol(Position^ target, bool shiftQueueCommand);
			bool Patrol(Position^ target);

			/// <summary>
			/// Orders the unit to hold its position.
			/// </summary>
			bool HoldPosition(bool shiftQueueCommand);
			bool HoldPosition();

			/// <summary>
			/// Orders the unit to stop
			/// </summary>
			bool Stop(bool shiftQueueCommand);
			bool Stop();

			/// <summary>
			/// Orders the unit to follow the specified unit.
			/// \see Unit::isFollowing
			/// </summary>
			bool Follow(Unit^ target, bool shiftQueueCommand);
			bool Follow(Unit^ target);

			/// <summary>
			/// Orders the unit to gather the specified unit (must be mineral or refinery type).
			/// \see Unit::isGatheringGas, Unit::isGatheringMinerals
			/// </summary>
			bool Gather(Unit^ target, bool shiftQueueCommand);
			bool Gather(Unit^ target);

			/// <summary>
			/// Orders the unit to return its cargo to a nearby resource depot such as a Command Center. Only
			/// workers that are carrying minerals or gas can be ordered to return cargo.
			/// \see Unit::isCarryingGas, Unit::isCarryingMinerals
			/// </summary>
			bool ReturnCargo(bool shiftQueueCommand);
			bool ReturnCargo();

			/// <summary>
			/// Orders the unit to repair the specified unit. Only Terran SCVs can be ordered to repair, and the
			/// target must be a mechanical Terran unit or building.
			/// \see Unit::isRepairing
			/// </summary>
			bool Repair(Unit^ target, bool shiftQueueCommand);
			bool Repair(Unit^ target);

			/// <summary>
			/// Orders the unit to burrow. Either the unit must be a Zerg Lurker, or the unit must be a Zerg ground
			/// unit and burrow tech must be researched.
			/// \see: Unit::unburrow, Unit::isBurrowed
			/// </summary>
			bool Burrow();

			/// <summary>
			/// Orders the burrowed unit to unburrow.
			/// \see: Unit::burrow, Unit::isBurrowed.
			/// </summary>
			bool Unburrow();

			/// <summary>
			/// Orders the unit to cloak.
			/// \see: Unit::decloak, Unit::isCloaked
			/// </summary>
			bool Cloak();

			/// <summary>
			/// Orders the unit to decloak.
			/// \see: Unit::cloak, Unit::isCloaked
			/// </summary>
			bool Decloak();

			/// <summary>
			/// Orders the unit to siege. Note: unit must be a Terran siege tank.
			/// \see Unit::unsiege, Unit::isSieged
			/// </summary>
			bool Siege();

			/// <summary>
			/// Orders the unit to unsiege. Note: unit must be a Terran siege tank.
			/// \see: Unit::unsiege, Unit::isSieged
			/// </summary>
			bool Unsiege();

			/// <summary>
			/// Orders the unit to lift. Note: unit must be a Terran building that can be lifted.
			/// \see Unit::land, Unit::isLifted
			/// </summary>
			bool Lift();

			/// <summary>
			/// Orders the unit to land. Note: unit must be a Terran building that is currently lifted.
			/// \see Unit::lift, Unit::isLifted
			/// </summary>
			bool Land(TilePosition^ target);

			/// <summary>
			/// Orders the unit to load the target unit.
			/// \see Unit::unload, Unit::unloadAll, Unit::getLoadedUnits, Unit:isLoaded
			/// </summary>
			bool Load(Unit^ target, bool shiftQueueCommand);
			bool Load(Unit^ target);

			/// <summary>
			/// Orders the unit to unload the target unit.
			/// \see Unit::load, Unit::unloadAll, Unit::getLoadedUnits, Unit:isLoaded
			/// </summary>
			bool Unload(Unit^ target);

			/// <summary>
			/// Orders the unit to unload all loaded units at the unit's current position.
			/// \see Unit::load, Unit::unload, Unit::unloadAll, Unit::getLoadedUnits, Unit:isLoaded
			/// </summary>
			bool UnloadAll(bool shiftQueueCommand);
			bool UnloadAll();

			/// <summary>
			/// Orders the unit to unload all loaded units at the specified location. Unit should be a Terran
			/// Dropship, Protoss Shuttle, or Zerg Overlord. If the unit is a Terran Bunker, the units will be
			/// unloaded right outside the bunker, like in the first version of unloadAll.
			/// \see Unit::load, Unit::unload, Unit::unloadAll, Unit::getLoadedUnits, Unit:isLoaded
			/// </summary>
			bool UnloadAll(Position^ target, bool shiftQueueCommand);
			bool UnloadAll(Position^ target);

			/// <summary>
			/// Works like the right click in the GUI
			/// </summary>
			bool RightClick(Position^ target, bool shiftQueueCommand);
			bool RightClick(Position^ target);

			/// <summary>
			/// Works like the right click in the GUI. Right click on a mineral patch to order a worker to mine,
			/// right click on an enemy to attack it
			/// </summary>
			bool RightClick(Unit^ target, bool shiftQueueCommand);
			bool RightClick(Unit^ target);

			/// <summary>
			/// Orders the SCV to stop constructing the building, and the building is left in a partially complete
			/// state until it is canceled, destroyed, or completed.
			/// \see Unit::isConstructing
			/// </summary>
			bool HaltConstruction();

			/// <summary>
			/// Orders the building to stop being constructed.
			/// \see Unit::beingConstructed
			/// </summary>
			bool CancelConstruction();

			/// <summary>Orders the unit to stop making the addon/// </summary>
			bool CancelAddon();

			/// <summary>
			/// Orders the unit to remove the specified unit from its training queue.
			/// \see Unit::train, Unit::cancelTrain, Unit::isTraining, Unit::getTrainingQueue
			/// </summary>
			bool CancelTrain(int slot);
			bool CancelTrain(); // -2 by default

			/// <summary>
			/// Orders the unit to stop morphing.
			/// \see Unit::morph, Unit::isMorphing
			/// </summary>
			bool CancelMorph();

			/// <summary>
			/// Orders the unit to cancel a research in progress.
			/// \see Unit::research, Unit::isResearching, Unit::getTech
			/// </summary>
			bool CancelResearch();

			/// <summary>
			/// Orders the unit to cancel an upgrade in progress.
			/// \see Unit::upgrade, Unit::isUpgrading, Unit::getUpgrade
			/// </summary>
			bool CancelUpgrade();

			/// <summary>
			/// Orders the unit to use a tech not requiring a target (ie Stim Pack). Returns true if it is a valid tech
			/// </summary>
			bool UseTech(Api::Enum::TechType tech);

			/// <summary>
			/// Orders the unit to use a tech requiring a position target (ie Dark Swarm). Returns true if it is a valid tech
			/// </summary>
			bool UseTech(Api::Enum::TechType tech, Position^ target);

			/// <summary>
			/// Orders the unit to use a tech requiring a unit target (ie Irradiate). Returns true if it is a valid tech
			/// </summary>
			bool UseTech(Api::Enum::TechType tech, Unit^ target);

			UnitGroup^ InRadius(double radius, Position^ position);
			UnitGroup^ InRegion(Terrain::Region^ region);
			UnitGroup^ OnlyNearestChokepoint(Chokepoint^ choke);
			UnitGroup^ OnlyNearestBaseLocation(BaseLocation^ location);
			UnitGroup^ OnlyNearestUnwalkablePolygon(Polygon^ polygon);

			UnitGroup^ Filter(FilterFlag f1);
			UnitGroup^ Filter(FilterFlag f1, FilterFlag f2);
			UnitGroup^ Filter(FilterFlag f1, FilterFlag f2, FilterFlag f3);
			UnitGroup^ Filter(FilterFlag f1, FilterFlag f2, FilterFlag f3, FilterFlag f4);
			UnitGroup^ Filter(FilterFlag f1, FilterFlag f2, FilterFlag f3, FilterFlag f4, FilterFlag f5);
			UnitGroup^ Filter(FliterAttributeScalar a, String^ compare, double value);
			UnitGroup^ Filter(FliterAttributeScalar a, String^ compare, int value);
			UnitGroup^ Filter(Player^ player);
			UnitGroup^ Filter(FilterAttributeUnit a, Unit^ unit);
			UnitGroup^ Filter(FilterAttributeType a, Api::Enum::UnitType type);
			UnitGroup^ Filter(FilterAttributeType a, Api::Enum::TechType type);
			UnitGroup^ Filter(FilterAttributeType a, Api::Enum::UpgradeType type);
			UnitGroup^ Filter(FilterAttributeOrder a, Api::Enum::OrderType type);
			UnitGroup^ Filter(FilterAttributePosition a, Position^ position);
			UnitGroup^ Filter(FilterAttributeTilePosition a, TilePosition^ position);

			UnitGroup^ FilterNot(FilterFlag f1);
			UnitGroup^ FilterNot(FilterFlag f1, FilterFlag f2);
			UnitGroup^ FilterNot(FilterFlag f1, FilterFlag f2, FilterFlag f3);
			UnitGroup^ FilterNot(FilterFlag f1, FilterFlag f2, FilterFlag f3, FilterFlag f4);
			UnitGroup^ FilterNot(FilterFlag f1, FilterFlag f2, FilterFlag f3, FilterFlag f4, FilterFlag f5);
			UnitGroup^ FilterNot(FliterAttributeScalar a, String^ compare, double value);
			UnitGroup^ FilterNot(FliterAttributeScalar a, String^ compare, int value);
			UnitGroup^ FilterNot(Player^ player);
			UnitGroup^ FilterNot(FilterAttributeUnit a, Unit^ unit);
			UnitGroup^ FilterNot(FilterAttributeType a, Api::Enum::UnitType type);
			UnitGroup^ FilterNot(FilterAttributeType a, Api::Enum::TechType type);
			UnitGroup^ FilterNot(FilterAttributeType a, Api::Enum::UpgradeType type);
			UnitGroup^ FilterNot(FilterAttributeOrder a, Api::Enum::OrderType type);
			UnitGroup^ FilterNot(FilterAttributePosition a, Position^ position);
			UnitGroup^ FilterNot(FilterAttributeTilePosition a, TilePosition^ position);

			//commented is not implemented in BWSAL
			static UnitGroup^ operator + (UnitGroup^ first, UnitGroup^ second); // Union
			//static UnitGroup^ operator | (UnitGroup^ first, UnitGroup^ second); // Union
			static UnitGroup^ operator * (UnitGroup^ first, UnitGroup^ second); // Intersection
			//static UnitGroup^ operator & (UnitGroup^ first, UnitGroup^ second); // Intersection
			static UnitGroup^ operator ^ (UnitGroup^ first, UnitGroup^ second); // Symmetric difference
			static UnitGroup^ operator - (UnitGroup^ first, UnitGroup^ second); // Difference
			static UnitGroup^ operator += (UnitGroup^ first, UnitGroup^ second); // Union
			//static UnitGroup^ operator |= (UnitGroup^ first, UnitGroup^ second); // Union
			static UnitGroup^ operator *= (UnitGroup^ first, UnitGroup^ second); // Intersection
			//static UnitGroup^ operator &= (UnitGroup^ first, UnitGroup^ second); // Intersection
			static UnitGroup^ operator ^= (UnitGroup^ first, UnitGroup^ second); // Symmetric difference
			static UnitGroup^ operator -= (UnitGroup^ first, UnitGroup^ second); // Difference
		};

		[ExtensionAttribute]
		public ref class UnitGroupExtension abstract sealed
		{
		public:
			[ExtensionAttribute]
			static UnitGroup^ ToUnitGroup(ICollection<Unit^>^ units);
		};

		UnitGroup^ ConvertUnitGroup(BWSAL::UnitGroup group);
	}
}
